/**
 * Copyright (c) Daniel Centore
 *
 * ex: set filetype=java expandtab tabstop=4 shiftwidth=4 :
 *
 * This program is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version.
 *
 * This code is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * This code is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * by the Free Software Foundation.
 */
package ronp.data;

import java.util.ArrayList;

import javax.swing.AbstractListModel;
import javax.swing.JList;
import javax.swing.ListModel;
import javax.swing.event.ListDataListener;

/**
 * An {@link ArrayList} bound to a {@link ListModel} so one can have changes in
 * the {@link ArrayList} immediately show up in the {@link ListModel} (and then
 * a {@link JList}, for example).
 * 
 * @author Daniel Centore
 *
 * @param <E> The type of data to store
 */
public class BoundListModel<E> extends ArrayList<E> implements ListModel
{
	private static final long serialVersionUID = 1L;

	private MyModel model = new MyModel();
	private BoundListModelModifier<E> modifier;

	/**
	 * Creates a {@link BoundListModel} with default modifier (none)
	 */
	public BoundListModel()
	{
		modifier = null;
	}

	/**
	 * Creates {@link BoundListModel} with a {@link BoundListModelModifier}
	 * @param mod The {@link BoundListModelModifier} to attach to it
	 */
	public BoundListModel(BoundListModelModifier<E> mod)
	{
		modifier = mod;
	}

	@Override
	public void addListDataListener(ListDataListener l)
	{
		model.addListDataListener(l);
	}

	@Override
	public Object getElementAt(int index)
	{
		return model.getElementAt(index);
	}

	@Override
	public int getSize()
	{
		return super.size();
	}

	@Override
	public void removeListDataListener(ListDataListener l)
	{
		model.removeListDataListener(l);
	}

	@Override
	public boolean add(E arg0)
	{
		boolean result = super.add(arg0);
		if (result)
			model.fireIntervalAdded(this, this.getSize() - 1, this.getSize() - 1);

		return result;
	}

	@Override
	public void add(int arg0, E arg1)
	{
		super.add(arg0, arg1);
		model.fireIntervalAdded(this, arg0, arg0);
	}

	@Override
	public void clear()
	{
		super.clear();
	}

	@Override
	public boolean remove(Object arg0)
	{
		int index = super.indexOf(arg0);

		try
		{
			this.remove(index);
		} catch (Exception e)
		{
			return false;
		}

		return true;
	}

	@Override
	public E remove(int arg0)
	{
		E result = super.remove(arg0);

		model.fireIntervalRemoved(this, arg0, arg0);

		return result;
	}

	@Override
	public E set(int arg0, E arg1)
	{
		E result = super.set(arg0, arg1);

		model.fireContentsChanged(this, arg0, arg0);

		return result;
	}

	/**
	 * We just use this as a workaround because one cannot
	 * extend 2 classes (damn Java)
	 * 
	 * @author Daniel Centore
	 *
	 */
	private class MyModel extends AbstractListModel
	{
		private static final long serialVersionUID = 1L;

		@Override
		public Object getElementAt(int index)
		{
			if (modifier != null)
				return modifier.modify(BoundListModel.this.get(index));

			return BoundListModel.this.get(index);
		}

		@Override
		public int getSize()
		{
			return BoundListModel.this.getSize();
		}

		// the stuff below here just increases visibility //
		@Override
		public void fireContentsChanged(Object source, int index0, int index1)
		{
			super.fireContentsChanged(source, index0, index1);
		}

		@Override
		public void fireIntervalAdded(Object source, int index0, int index1)
		{
			super.fireIntervalAdded(source, index0, index1);
		}

		@Override
		public void fireIntervalRemoved(Object source, int index0, int index1)
		{
			super.fireIntervalRemoved(source, index0, index1);
		}

		// end visibility //

	}

}
